﻿using Algorithm.Check;
using Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Algorithm.Check.ExtensionForCRC16;

namespace NewDevice.RS485
{
    /// <summary>
    /// RU18_10001程控电源控制类
    /// 提供基于Modbus协议的程控电源通信功能
    /// </summary>
    public class RU18_10001 : IDeviceOnline
    {
        /// <summary>
        /// 设备名称
        /// </summary>
        private string deviceName = "RU18_10001程控电源";

        /// <summary>
        /// 串口通信对象
        /// </summary>
        private readonly SerialBase serial;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="bus">串口通信对象</param>
        public RU18_10001(SerialBase bus)
        {
            this.serial = bus;
        }

        /// <summary>
        /// 设备名称
        /// </summary>
        public string Device
        {
            set { deviceName = value; }
        }

        /// <summary>
        /// 设置输出电压
        /// </summary>
        /// <param name="slaveId">从机ID</param>
        /// <param name="vol">电压值(单位：mV)</param>
        /// <exception cref="Exception">当设置失败或返回数据异常时抛出</exception>
        public void SetVoltage(int slaveId, int vol)
        {
            Mylog.Info(deviceName: deviceName, message: $"从机[{slaveId}]设置电压[{vol}]");
            byte[] data = new byte[] {
                (byte)slaveId,
                0x06, // 功能码:写单个寄存器
                0x00, 0x95, // 寄存器地址:0x0095(输出电压设置寄存器)
                (byte)((vol & 0xff00) >> 8), // 电压值高字节
                (byte)(vol & 0xff), // 电压值低字节
            };
            byte[] cmd = data.WithCRC16(CRC16Type.Modbus).ToArray();
            serial.deviceName = deviceName;
            var recv = serial.SendBytesAndWaitResponse(cmd, ReceivedDataCheck.FuncModbusCRC16Check);
            if (!recv.SequenceEqual(cmd))
            {
                throw new Exception($"{deviceName} SetVoltage 返回数据异常");
            }
        }

        /// <summary>
        /// 读取当前输出电压
        /// </summary>
        /// <param name="slaveId">从机ID</param>
        /// <returns>当前输出电压值(单位：mV)</returns>
        /// <exception cref="Exception">当读取失败或返回数据异常时抛出</exception>
        public int GetVoltage(int slaveId)
        {
            byte[] data = new byte[] {
                (byte)slaveId,
                0x03, // 功能码:读保持寄存器
                0x00, 0x95, // 寄存器地址:0x0095(输出电压读取寄存器)
                0x00, 0x01 // 读取寄存器数量:1
            };
            byte[] cmd = data.WithCRC16(CRC16Type.Modbus).ToArray();
            serial.deviceName = deviceName;
            var recv = serial.SendBytesAndWaitResponse(cmd, ReceivedDataCheck.FuncModbusCRC16Check);
            if (recv.Length != 7)
            {
                throw new Exception($"{deviceName} GetVoltage 返回数据异常");
            }
            var vol = ((recv[3] & 0xFF) << 8) | (recv[4] & 0xFF);
            Mylog.Info(deviceName: deviceName, message: $"从机[{slaveId}]读取电压[{vol}]");
            return vol;
        }

        /// <summary>
        /// 设置输出电流
        /// </summary>
        /// <param name="slaveId">从机ID</param>
        /// <param name="current">电流值(单位：A)</param>
        /// <exception cref="Exception">当设置失败或返回数据异常时抛出</exception>
        public void SetCurrent(int slaveId, double current)
        {
            Mylog.Info(deviceName: deviceName, message: $"从机[{slaveId}]设置电流[{current}]");
            int tmp = (int)(current * 1000); // 转换为mA单位
            byte[] data = new byte[] {
                (byte)slaveId,
                0x06, // 功能码:写单个寄存器
                0x00, 0x96, // 寄存器地址:0x0096(输出电流设置寄存器)
                (byte)((tmp & 0xff00) >> 8), // 电流值高字节
                (byte)(tmp & 0xff), // 电流值低字节
            };
            byte[] cmd = data.WithCRC16(CRC16Type.Modbus).ToArray();
            serial.deviceName = deviceName;
            var recv = serial.SendBytesAndWaitResponse(cmd, ReceivedDataCheck.FuncModbusCRC16Check);
            if (!recv.SequenceEqual(cmd))
            {
                throw new Exception($"{deviceName} SetCurrent 返回数据异常");
            }
        }

        /// <summary>
        /// 读取当前输出电流
        /// </summary>
        /// <param name="slaveId">从机ID</param>
        /// <returns>当前输出电流值(单位：A)</returns>
        /// <exception cref="Exception">当读取失败或返回数据异常时抛出</exception>
        public double GetCurrent(int slaveId)
        {
            byte[] data = new byte[] {
                (byte)slaveId,
                0x03, // 功能码:读保持寄存器
                0x00, 0x96, // 寄存器地址:0x0096(输出电流读取寄存器)
                0x00, 0x01 // 读取寄存器数量:1
            };
            byte[] cmd = data.WithCRC16(CRC16Type.Modbus).ToArray();
            serial.deviceName = deviceName;
            var recv = serial.SendBytesAndWaitResponse(cmd, ReceivedDataCheck.FuncModbusCRC16Check);
            if (recv.Length != 7)
            {
                throw new Exception($"{deviceName} GetCurrent 返回数据异常");
            }
            var cur = (double)(((recv[3] & 0xFF) << 8) | (recv[4] & 0xFF)) / 1000; // 转换为A单位
            Mylog.Info(deviceName: deviceName, message: $"从机[{slaveId}]读取电流[{cur}]");
            return cur;
        }

        /// <summary>
        /// 设置电源输出状态
        /// </summary>
        /// <param name="slaveId">从机ID</param>
        /// <param name="isOn">true:开启输出, false:关闭输出</param>
        /// <exception cref="Exception">当设置失败或返回数据异常时抛出</exception>
        public void SetStatus(int slaveId, bool isOn)
        {
            Mylog.Info(deviceName: deviceName, message: $"从机[{slaveId}]设置电源输出状态[{isOn}]");
            byte[] data = new byte[] {
                (byte)slaveId,
                0x05, // 功能码:写单个线圈
                0x00, 0x85, // 线圈地址:0x0085(输出控制寄存器)
                (byte)(isOn ? 0xff : 0x00), // 控制值
                0x00 // 填充字节
            };
            byte[] cmd = data.WithCRC16(CRC16Type.Modbus).ToArray();
            serial.deviceName = deviceName;
            var recv = serial.SendBytesAndWaitResponse(cmd, ReceivedDataCheck.FuncModbusCRC16Check);
            if (!recv.SequenceEqual(cmd))
            {
                throw new Exception($"{deviceName} SetStatus 返回数据异常");
            }
        }

        /// <summary>
        /// 检查设备是否在线
        /// </summary>
        /// <param name="slaveId">从机ID，默认为1</param>
        /// <returns>true:设备在线, false:设备离线</returns>
        public bool IsDeviceOnline(int slaveId = 1)
        {
            try
            {
                var sts = GetVoltage(slaveId); // 尝试读取电压值检测设备是否在线
                return true;
            }
            catch (Exception ex)
            {
                Mylog.Error(message: $"{deviceName}不在线,{ex.Message}", ex: ex);
                return false;
            }
        }
    }
}